1.線程的實現(xiàn)
- 實現(xiàn)線程的第一種方式,繼承thread類
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 實現(xiàn)線程的第二種方式,實現(xiàn)Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 調(diào)用線程
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start(); //啟動線程
//推薦
MyRunnable mr = new MyRunnable();
Thread t2 = new Thread(mr);
t2.run();
}
sleep()方法: 線程的休眠在當前線程的執(zhí)行中,暫停指定的毫秒數(shù),釋放CPU的時間片,具體取決于系統(tǒng)定時器和調(diào)度程序的精度和準確性,線程不會丟失任何監(jiān)視器的所有權(quán)
2.中斷線程
實例:
我們先來創(chuàng)建兩個線程
class MyRunnable2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable3 implements Runnable{
public boolean flag = true;
public MyRunnable3(){
flag = true;
}
@Override
public void run() {
int i = 0;
while(flag){
System.out.println(Thread.currentThread().getName()+"---"+(i++));
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
然后調(diào)用
public static void main(String[] args) {
MyRunnable2 run1 = new MyRunnable2();
Thread t = new Thread(run1);
t.start();
MyRunnable3 rm3 = new MyRunnable3();
Thread t3 = new Thread(rm3);
t3.start();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+"<->"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i==20){
try {
t.join(); //讓t線程執(zhí)行完畢
} catch (InterruptedException e) {
e.printStackTrace();
//t3.interrupt();
rm3.flag = false;
}
}
}
}
表示main和t這兩個線程, 先是main和t同時執(zhí)行,等到main執(zhí)行到i=20時,main停下,等待t執(zhí)行完畢后,main再繼續(xù)執(zhí)行
中斷方法
join方法:
加入線程,讓調(diào)用的線程先執(zhí)行指定時間或執(zhí)行完畢
中斷線程:
(1) 使用interrupt()方法來中斷線程,設置一個中斷狀態(tài)(標記)sleep會自動清除中斷標記位,
(2) 自定義標記的方式 推薦
3 守護線程與yield
public static void main(String[] args) {
MyRunnable4 rm4 = new MyRunnable4();
Thread t4 = new Thread(rm4);
t4.setName("主線程"); //設置線程名稱
//設置線程優(yōu)先級,優(yōu)先級高可以提高線程搶占CPU時間片的概率
t4.setPriority(Thread.MAX_PRIORITY);
//線程可以分為守護線程和用戶線程,當進程中沒有用戶線程時,JVM會退出
t4.setDaemon(true); //把線程設置為守護線程
t4.start();
for (int i = 0; i < 20; i++) {
System.out.println("main--"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
線程的其他方法
- public final void setDaemon(boolean on) on=true時表示設置為守護線程
- public final boolean isDaemon()測試這個線程是否為守護線程
- public static void yield()暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程
- long getId() 返回該線程的標識符
- String getName() 返回該線程的名稱
- void setName(String name) 設置該線程的名稱
- boolean isAlive()他測試線程是否處于活動狀態(tài)
- void setPriority(int newPriority) 更改線程的優(yōu)先級
- static int MAX_PRIORITY 線程可以具有的最高優(yōu)先級
- static int MIN_PRIORITY 線程可以具有的最低優(yōu)先級
- static int NORM_PRIORITY 分配給線程的默認優(yōu)先級
4 線程同步
1. 多線程共享數(shù)據(jù)時,會發(fā)生線程不安全的情況
2. 多線程共享數(shù)據(jù)必須使用同步
線程同步的三種方法
1. 同步代碼塊
synchronized (this) {
for (int i = 0; i < 300; i++) {
if (ticket > 0) {
ticket--;
try {
Thread.sleep(1000);
} catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"您購買的票已剩余:" +
ticket + "張");
}
}
}
注意: synchronized()里面是一個同步鎖,只要是一個對象就可以了
2.同步方法
public synchronized void method() {
for (int i = 0; i < 300; i++) {
if (ticket > 0) {
ticket--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"您購買的票已剩余:" + ticket +
"張");
}
}
}
注意: 實際上同步鎖是this
3. 使用Lock(更靈活的控制)
ReentrantLock lock = new ReentrantLock();//互斥鎖
public void method2() {
lock.lock();//鎖定
try {
for (int i = 0; i < 300; i++) {
if (ticket > 0) {
ticket--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"您購買的票已剩余:" + ticket + "張");
}
}
} finally {
lock.unlock(); //釋放鎖
}
}
同步準則:
- 使代碼塊保持簡短,把不隨線程變化的預處理和后處理移出synchronized代碼塊
- 不要阻塞,如InputStream.read().
- 在持有鎖的時候,不要對其他對象調(diào)用同步方法
5 線程池
public static void main(String[] args) {
//創(chuàng)建線程池
//1. 創(chuàng)建一個單線程的線程池
//ExecutorService es =Executors.newSingleThreadExecutor();
//2. 創(chuàng)建一個固定大小的線程池
//ExecutorService es =Executors.newFixedThreadPool(3);
//3. 創(chuàng)建一個可緩存的線程池
//ExecutorService es =Executors.newCachedThreadPool();
//4. 創(chuàng)建一個大小無限的線程池
ScheduledExecutorService sche = Executors.newScheduledThreadPool(3);
ExecutorService es = sche;
es.execute(new MyRunnable6());
es.execute(new MyRunnable6());
es.shutdown();
}
6 生產(chǎn)者消費者案例
public class ThreadDemo6 {
public static void main(String[] args) {
Food food = new Food();
Producter p = new Producter(food);
Customers c = new Customers(food);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
/**
* 消費者
*/
class Customers implements Runnable {
private Food food;
public Customers(Food food) {
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
food.get();
}
}
}
/**
* 生產(chǎn)者
*/
class Producter implements Runnable {
private Food food;
public Producter(Food food) {
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
food.set("鍋包肉", "酸甜口味");
} else {
food.set("佛跳墻", "美容養(yǎng)顏");
}
}
}
}
class Food {
private String name;
private String desc;
private boolean flag = true; //true表示可以生產(chǎn),
// false表示可以消費
/**
* 生產(chǎn)產(chǎn)品
*/
public synchronized void set(String name,
String desc) {
//不能生產(chǎn)
if (!flag) {
try {
this.wait(); //線程進入等待狀態(tài),
// 釋放監(jiān)視器所有權(quán)(對象鎖)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (flag) {
this.setName(name);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setDesc(desc);
flag = false;
this.notify(); //喚醒等待的線程(隨機的其中一個)
}
}
/**
* 消費產(chǎn)品
*/
public synchronized void get() {
//不能消費
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!flag) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + "--" + this.getDesc());
flag = true;
this.notifyAll();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Food{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
public Food(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Food() {
super();
}
}
- 兩個線程協(xié)同工作: 先生產(chǎn),再消費
- wait()里面如果加入時間,則能自動喚醒
- 面試題: sleep和 wait的 區(qū)別?
- sleep:讓線程進入休眠狀態(tài),讓出CPU的時間片,不釋放對象監(jiān)視器的所有權(quán)
- wait: 讓線程進入等待狀態(tài),讓出CPU的時間片,并釋放對象監(jiān)視器的所有權(quán),
-
等待其他線程通過notify()方法來喚醒
java-thread.jpg
