①通过实现Runnable接口
public class ThreadDemo{
public static void main(String [] args) {
TestThread t=new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable {
int i=100;
public void run(){
while(true) {
if (i > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
i--;
System.out.println(Thread.currentThread().getName() +
" is selling ticket " + i);
}
else {
break;
}
}
}
}
结果:
Thread-0 is selling ticket 99
Thread-1 is selling ticket 98
·
·
·
Thread-2 is selling ticket 5
Thread-0 is selling ticket 4
Thread-1 is selling ticket 3
Thread-3 is selling ticket 2
Thread-2 is selling ticket 1
Thread-0 is selling ticket 0
Thread-1 is selling ticket -1
Thread-3 is selling ticket -2
Thread-2 is selling ticket -3
解释:
1.通过new Thread( new TestThread【实现了Runnable接口的类】)的形式创建线程
2.特殊地,类似上文的形式。将TestThread t=new TestThread()单独写出来,在其后可一次性创建和开始多个线程。
TestThread内普通变量在不同线程中共享
但是由于线程之间没有
加锁限制
,结果有103行,多了3行
②通过继承Thread类
public class ThreadDemo
{
public static void main(String [] args)
{
new TestThread().start();
new TestThread().start();
new TestThread().start();
new TestThread().start();
}
}
class TestThread extends Thread
{
private static int i=100; //static变量是共享的,所有的线程共享
public void run()
{
while(true)
{
if(i>0)
{
try {
Thread.sleep(100);
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName() + i);
i = i - 1;
}
else
{
break;
}
}
}
}
结果:
Thread-1 is selling ticket 100
Thread-0 is selling ticket 99
Thread-3 is selling ticket 98
·
·
·
Thread-0 is selling ticket 5
Thread-2 is selling ticket 4
Thread-1 is selling ticket 3
Thread-3 is selling ticket 2
Thread-0 is selling ticket 1
Thread-2 is selling ticket 0
Thread-1 is selling ticket -1
Thread-3 is selling ticket -2
解释:
1. 直接通过new ThreadTest【继承了类Thread的类】.start()创建并开始线程
2. 普通变量之间无共享,只有ThreadTest类的变量声明为
static
才能实现共享
例子,无volatile,非共享。
public class tryThread {
public static void main(String args[]){
TestThread th=new TestThread();
Thread t1 = new Thread(th);
Thread t2 = new Thread(th);
Thread t3 = new Thread(th);
t1.start();
t2.start();
t3.start();
try{
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
th.flag=false;
System.out.println("main is exiting");
}
}
class TestThread implements Runnable{
boolean flag = true;
public void run() {
while (flag) {
}
System.out.println("Thread is exiting");
}
}
结果:
main is exiting
无限循环,需要自行终止程序。
(结果只输出一行主线程退出时的提示语句,而子线程一直在while循环里,没有检测到 flag 已经变化,也没有输出子线程退出时的提示语句)
注意:这里特别将run方法中的while循环写的很简单,因为太复杂会迷之检测到 flag 的变化,从而结束线程,得到Thread is exiting的正确结果
要让编译器来不及反应到flag的变化(大雾)
更 新 : 百 度 之 后 , 发 现 原 因 可 能 是 之 前 写 的 代 码 里 有 非 原 子 级 别 的 操 作 , 例 如 n + + , n = n + 1 , 可 能 类 似 v o l a t i l e 失 效 问 题 。 ( v o l a t i l e 失 效 问 题 详 情 见 下 ) \color{red}{更新:百度之后,发现原因可能是之前写的代码里有非原子级别的操作,例如n++,n=n+1,可能类似volatile失效问题。(volatile失效问题详情见下)} 更新:百度之后,发现原因可能是之前写的代码里有非原子级别的操作,例如n++,n=n+1,可能类似volatile失效问题。(volatile失效问题详情见下)
- 若线程是实现Runnable接口而来,则可使用volatile关键字
即在关键变量前,即boolean flag前加volatile
volatile boolean flag
特别注意:volatile的失效问题,即当用volatile关键字修饰的变量n进行n++,n=n+1等非原子级别操作时,volatile会失效。结果:
main is exiting
Thread is exiting
Thread is exiting
Thread is exiting
正确退出了
- 如果线程是继承Thread类而来,关键变量前使用static
- 工作缓存副本
见上文用volatile或static实现
再次提醒:注意volatile失效问题 - 关键步骤加锁限制
synchronized互斥加锁 (通用方法,简单但负担高)
防止多个线程访问同一个关键数据产生错误。【本文开头第一段代码多出3行的原因】
注意:一个线程访问synchronized代码块时。另一个线程可访问程序的非synchronized部分
- ⑴synchronized修饰函数
public class tryThread {
public static void main(String args[]){
TestThread th=new TestThread();
Thread t1 = new Thread(th);
Thread t2 = new Thread(th);
Thread t3 = new Thread(th);
t1.start();
t2.start();
t3.start();
try{
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
th.flag=false;
System.out.println("main is exiting");
}
}
class TestThread implements Runnable{
boolean flag = true;
String str="";
public void run() {
while (flag) {
fun();
}
System.out.println("Thread is exiting");
}
private synchronized void fun() {
}
}
结果:
main is exiting
Thread is exiting
Thread is exiting
Thread is exiting
正确输出。
- ⑵synchronized修饰代码块
String str="";
synchronized (str) {
fun();
}
③线程池
一个线程必有所属线程组
待续……