java併發基礎
Callable
首先我們來理一理Future和Runable的關係:
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
也就是說FutureTask
間接的 實現了Runable
而利用FutureTask
:我們就有了線程進行,取消、判斷線程運行狀態、獲取結果等功能。實現如下:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get();
V get(long timeout, TimeUnit unit);
}
而FutureTask
間接的 實現了Runable
: 故我們調用 的 Callable
中的call
方法,其實是在Runable
裏面的run()
方法中執行的;
public void run() {
......
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
......
}
if (ran)
set(result);
}
} finally {
......
}
}
接下來是HelloCallabe的簡單操作:
public class HelloThread{
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTest callableTest = new CallableTest();
FutureTask<Integer> ft = new FutureTask<Integer>(callableTest);
Thread thread = new Thread(ft);
thread.start();
System.out.println(ft.get());
}
}
class CallableTest implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return 123;
}
}
線程的停止stop()
想要線程停止,我們最好在內部調用使其停止,簡單實現如下:
結合Thred.sleep()
的一個倒計時功能;
具體還是stop()
方法
public class ThreadStop {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
new Thread(test).start();
for(int i = 0; i < 10; i++){
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + i);
}
test.stop();
}
}
class Test implements Runnable{
boolean flag = true;
@Override
public void run() {
while(flag){
try {
Thread.sleep(1000);
Date date = new Date();
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop(){
this.flag = false;
}
}
線程的禮讓yield()
對靜態方法 Thread.yield() 的調用聲明瞭當前線程已經完成了生命週期中最重要的部分,可以切換給其它線程來執行。該方法只是對線程調度器的一個建議,而且也只是建議具有相同優先級的其它線程可以運行。
public class Yield {
public static void main(String[] args) {
new Thread(new R(),"a").start();
new Thread(new R(),"b").start();
}
}
class R implements Runnable{
@Override
public void run() {
System.out.println("+++++++++開始了++++++++++++" + Thread.currentThread().getName());
Thread.yield();
System.out.println("+++++++++結束++++++++++++" + Thread.currentThread().getName());
}
}
線程的強制插入Join()
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new R1());
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + i);
if(i == 20)thread.join();
}
}
}
class R1 implements Runnable{
@Override
public void run() {
int i = 100;
while(i-- > 0){
System.out.println("+++++++++開始了++++++++++++" + Thread.currentThread().getName());
System.out.println("+++++++++結束++++++++++++" + Thread.currentThread().getName());
}
}
}
使用state觀察線程狀態:
Therad.getState();
System.out.println(state);
線程設置優先級setPriority()
public class Priority {
public static void main(String[] args) {
Thread t1 = new Thread(new R3(),"t1");
Thread t2 = new Thread(new R3(),"t2");
Thread t3 = new Thread(new R3(),"t3");
Thread t4 = new Thread(new R3(),"t4");
Thread t5 = new Thread(new R3(),"t5");
Thread t6 = new Thread(new R3(),"t6");
t1.setPriority(1);
t1.start();
t2.setPriority(2);
t2.start();
t3.setPriority(3);
t3.start();
t4.setPriority(5);
t4.start();
t5.setPriority(7);
t5.start();
t6.setPriority(10);
t6.start();
}
}
class R3 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "正在運行 " + Thread.currentThread().getPriority());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
守護線程deamon
- 線程分爲用戶線程和守護線程
- 虛擬機必須確保用戶線程執行完畢
- 虛擬機不用等待守護線程執行完畢
- 如,操作日誌,監控內存,垃圾回收等
鎖synchronized
- 鎖方法
- 索同步代碼塊(對象)
鎖Lock
- Lock只能鎖代碼塊,但是其性能更好
public class HelloTecket {
public static void main(String[] args) throws InterruptedException {
Tic tic = new Tic();
new Thread(tic,"t1").start();
new Thread(tic,"t2").start();
new Thread(tic,"t3").start();
}
}
class Tic implements Runnable{
static int ticket = 10;
static boolean flag = true;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(flag){
try{
lock.lock();
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售出了第" + ticket-- + " 張票" + Thread.currentThread().getName());
}else{
stop();
}
}finally {
lock.unlock();
}
}
}
private static void stop(){
flag = false;
}
}
多線程生產者消費者簡單模型
public class Pro {
public static void main(String[] args) throws InterruptedException {
Food food = new Food();
new Thread(new Producer(food)).start();
new Thread(new Customer(food)).start();
}
}
enum F{
APPLE("蘋果"),CHEERY("櫻桃"),MANGO("芒果"),WATERMELON("西瓜");
private String name;
private F(String name){
this.name = name;
}
}
class Food{
private String[] Fruit = new String[12];
int count = 0;
private Random random = new Random();
public synchronized void add() throws InterruptedException {
if(count >= 10) {
System.out.println("緩衝區已滿++++++無法生產");
super.wait();
}
else{
Thread.sleep(100);
int rand = random.nextInt(4);
switch (rand){
case 0: Fruit[++count] = F.APPLE.toString();break;
case 1: Fruit[++count] = F.CHEERY.toString();break;
case 2: Fruit[++count] = F.MANGO.toString();break;
case 3: Fruit[++count] = F.WATERMELON.toString();break;
}
System.out.println("已生成第" + count + " 個水果" + Fruit[count]);
super.notifyAll();
}
}
public synchronized String pop() throws InterruptedException {
if(count <= 0){
super.wait();
return "沒有食物 : " + count;
}else{
Thread.sleep(100);
super.notifyAll();
int temp = count;
String fruit = Fruit[count--];
return "得到食物 : " + fruit + "他是第 " + temp + "個";
}
}
void test(){
for (String s : Fruit) {
System.out.println(s);
}
}
}
class Producer implements Runnable{
private Food food;
public Producer (Food food){
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
food.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Customer implements Runnable{
private Food food;
public Customer(Food food){
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println(food.pop());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
模擬數字的加減,使Num保持在0,1中
public class MathThread {
public static void main(String[] args) {
Resource re = new Resource();
Add add = new Add(re);
Sub sub = new Sub(re);
new Thread(add, "加法線程--A").start();
new Thread(add, "加法線程--B").start();
new Thread(sub, "減法線程---A").start();
new Thread(sub, "減法線程---B").start();
}
}
class Resource {
private volatile int num = 0;
private boolean flag = true;
public synchronized void add() throws Exception{
while(this.flag == false){
super.wait();
}
Thread.sleep(10);
this.num++;
System.out.println(Thread.currentThread().getName() + " " + this.num);
this.flag = false;
super.notify();
}
public synchronized void sub() throws Exception{
while(this.flag == true){
super.wait();
}
Thread.sleep(10);
this.num--;
System.out.println(Thread.currentThread().getName() + " " + this.num);
this.flag = true;
super.notify();
}
}
class Add implements Runnable{
private Resource resource;
public Add(Resource resource){
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
try {
this.resource.add();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Sub implements Runnable{
private Resource resource;
public Sub(Resource resource){
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
try {
this.resource.sub();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
被喚醒的線程,應該被繼續檢測,所以永遠在while循環而不是if語句中使用wait!
volatile
- 主要定義在屬性上的,表示此屬性爲直接操作,不進行副本處理
普通變量操作過程:
- 獲取原有變量的副本
- 對副本進行操作
- 將操作後的結果,保存到原始空間中
加上volatile的變量,表示直接操作原始變量,節約了拷貝副本的時間。
併發深入理解
synchronized
實現原理:保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區
使用方式:(重點)
- 定義在普通方法上: 表示進入同步代碼前,要獲得當前實例的鎖。
- 靜態同步方法: 表示是當前的class類。
- 同步方法塊: 表示鎖的是 括號裏的對象。
synchronized作用於靜態方法
public class TestTread {
public static void main(String[] args) {
final syncTest test = new syncTest();
/**
* 在普通方法上加鎖,鎖的是同一個實例
* 故操作同一個實例,有效。
*/
new Thread(()->test.method1()).start();
new Thread(()->test.method2()).start();
/**
* 不同的實例,失效。
*/
// new Thread(()->new syncTest().method1()).start();
// new Thread(()->new syncTest().method2()).start();
}
}
class syncTest{
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
}
synchronized作用於靜態方法
public class StaticThread{
//共享資源
static int i = 0;
/**
* synchronized 修飾實例方法
*/
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new St(),"s1");
Thread t2 = new Thread(new St(),"s2");
t1.start();
t2.start();
t1.join(); //加上join的使 主線程進入等待池,需要等待t1,t2執行完畢,方便後面的St.i 靜態變量的輸出
t2.join();
Thread.sleep(2000);
System.out.println(St.i);
}
}
class St implements Runnable {
static int i = 0;
public static synchronized void increase(){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
@Override
public void run() {
for (int j = 0; j < 100; j++) {
System.out.println(Thread.currentThread().getName() + " " + i);
increase();
}
}
}
synchronized作用於同步代碼塊
public class BlockThread implements Runnable {
static BlockThread instance=new BlockThread();
static int i=0;
@Override
public void run() {
//省略其他耗時操作....
//使用同步代碼塊對變量i進行同步操作,鎖對象爲instance
synchronized(instance){ // 可修改爲 this 或者 當前類的 class
for(int j=0;j<10000;j++){
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
join()方法
大部分看起來好像是:t.join()方法會使所有其它線程都暫停並等待t的執行完畢。
經過上面 :synchronized作用於靜態方法 代碼看到結果。
t1.start();
t2.start();
t1.join();
t2.join();
上述順序中,t1,t2交替運行。
而若改成:
t1.start();
t1.join();
t2.start();
t2.join();
則是 t1先執行完畢,t2再執行完畢。
join()源代碼:
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0); //join()相當於調用了join(0)
}
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0); //join(0)等同於wait(0),即wait無限時間直到被notify
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
借鑑其他博客得知:在哪個線程中調用 x.join()
則該線程掛起。
上述代碼在 主線程中調用t1.join()
,故主線程掛起,等待t1執行完畢,才繼續執行下面代碼,所以若是這樣:
t1.start();
t2.start();
t1.join();
t2.join();
則t1,t2交互進行。
參考博文:https://blog.csdn.net/zjy15203167987/article/details/82531772
notify()
和notifyAll()
鎖池和等待池:
-
鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),由於這些線程在進入對象的synchronized方法之前必須先獲得該對象的鎖的擁有權,但是該對象的鎖目前正被線程A擁有,所以這些線程就進入了該對象的鎖池中。
-
等待池:假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖後,進入到了該對象的等待池中
鎖池:沒有得到鎖的線程,進入鎖池,等待釋放後,可以去競爭鎖。
等待池:被調用wait()
進入等待池,不能去競爭鎖,必須先被喚醒,才能去競爭鎖。
notify喚醒一個等待的線程;notifyAll喚醒所有等待的線程。
參考博文:https://blog.csdn.net/djzhao/article/details/79410229