我们知道创建线程有两种方法:继承Thread类,和实现Runnable接口,有什么区别呢?
首先,我们要回顾一下Java线程的知识:
终止(terminate)、消亡:
注意:suspend() stop() resume()在新特性中均已过时。
static Thread currentThread()
class CurrentThreadDemo{
public static void main(String[] args){
Thread t = Thread.currentThread();
System.out.println("Current thread: " + t);
t.setName("BJTShang’s Thead");
System.out.println("After name changed: " + t);
try{
for(int n=5;n>0;n--){
System.out.println(n);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println("Main thread interruped");
}
}
}
输出:
Current thread: Thread[main,5,main]
After name changed: Thread[BJTShang’s Thead,5,main]
5
4
3
2
1
在本程序中,对主线程的引用通过调用currentThread()方法获得,用局部变量t保存该引用;通过调用Thread类的各子方法setName,sleep控制主线程。线程信息输出格式:Thread[线程名称,优先级,组名称]
通过实例化一个Thread对象来创建新线程:继承Thread类,或者实现Runnable接口;
重写新线程的入口run()方法,调用start方法启动新线程;(通常主线程必须是结束运行的最后一个进程:保证主线程运行足够时间)
1. seName()getName()方法:默认名称“Thread-编号”;
2. 也可以构造使用super调用Thread父类带字符串参数构造方法的构造方法;
3. ```
class Sub extends Thread{
Sub(String name){
super(name);//调用父类Thread的构造方法,在创建Sub类对象时,自定义线程名称
}
}
```
class SubThread extends Thread{
public void run(){
System.out.println("SubThread Run");
}
public static void main(String[] agrs){
SubThread st = new SubThread();//创建好一个线程;
st.start();//start()方法两个作用:启动该线程对象,调用该对象中的run()方法;
//st.run();main方法中的主线程调用run方法,但是没有启动SubThread线程;依然属于单线程程序;
}
}
Thread类中的run方法,用于存储线程要运行的方法(主线程要运行的代码存放于main方法)
卖票被卖出多张的问题:
class Ticket extends Thread{
private int ticket = 100;
//private static int ticket = 100; 将ticket设置成静态,可以防止同一张票被卖出多张:相同的数据(实际上每个线程在内存中数据的位置是不同的(每一个线程都单独开辟了一块内存区域),是不同的数据,只是数值相同)被多个线程共享
//如果定义成static,则实现了与类的静态绑定,避免数据被共享,但是:static的生命周期太长,占用资源
public void run(){
while (true){
if (ticket > 0)
{
System.out.println(currentThread().getName() + "..." + ticket--);
}
}
}
public static void main(String[] args)
{
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
1. 定义类实现Runnable接口;
2. 覆盖Runnable接口中的run方法;
* 将线程要执行的代码存放在该run方法中
3. 通过Runnable子类对象,创建Thread类线程对象;
4. 将Runnable接口子类对象作为参数传递给Thread类的构造函数;
* 因为要创建的线程所执行的代码run方法属于Runnable接口的子类对象,如果想让新创建的线程去执行run方法中的代码,必须将其所属对象传递给新线程;
5. 调用Thread类的start方法开启线程并调用Runnable接口子类中实现的run方法;
class Ticket implements Runnable{
private int ticket = 100;
public void run(){
while (true){
if (ticket > 0)
{
System.out.println(Thread.currentThread().getName() + "..." + ticket--);
}
}
}
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
两个或两个以上的线程需要共享资源,需要一种办法来确定资源在某一时刻仅被一个线程占用,否则会出现安全问题,例如:
class Ticket implements Runnable{
private int ticket = 10;
public void run(){
while (true){
if (ticket > 0){
try{
Thread.sleep(300);//此时间内,多个线程可以同步进入run方法,出现负票
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "..." + ticket--);
}
}
}
public static void main(String[] args){
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
必须清楚共享数据在哪里使用;
使用synchronized关键字:进入某一对象的管程,就是调用被synchronized关键字修饰的方法;在此同步方法内部,所有试图调用该方法的同实例的其他线程必须等待;直到拥有管程的线程从同步方法中退出,就可以将对象(锁)的控制权放弃给其他等待的线程;那些等待的线程被放在线程池中,并根据等待的先后顺序依次被放入管程中。
单例设计模式,总结:
线程死锁:避免
* 出现:同步中嵌套同步;
class Test implements Runnable{
private boolean flag;
Test(boolean flag){
this.flag = flag;
}
public void run(){
if(flag){
synchronized(MyLock.locka){
System.out.println("if locka");
synchronized(MyLock.lockb){
System.out.println("if loakb");
}
}
}
else{
synchronized(MyLock.lockb){
System.out.println("else lockb");
synchronized(MyLock.locka){
System.out.println("else locka");
}
}
}
}
}
class MyLock {
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest {
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
多个线程在操纵一个资源
class Res{
String name;
String gender;
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while (true){
if(x==0){
r.name = "晨晨";
r.gender = "男的";
}else{
r.name = "红题";
r.gender = "女的";
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
System.out.println(r.name + "..." + r.gender);
}
}
}
class ThreadCom{
public static void main(String[] agrs){
Res s = new Res();
Input i = new Input(s);
Output o = new Output(s);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
Input生产数据的同时,Output在消费数据,不安全。解决方法:给两个线程加上同一个锁;
class Res{
String name;
String gender;
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
synchronized(r){
if(x==0){
r.name = "BJT";
r.gender = "男的";
}
else{
r.name = "Shenqidemao";
r.gender = "女的";
}
x = (x+1)%2;
}
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
synchronized(r){//两个线程使用同一个锁,对象相同就行,r对象引用也可以换成Input.class
System.out.println(r.name + "..." + r.gender);
}
}
}
}
class ThreadCom{
public static void main(String[] agrs){
Res s = new Res();
Input i = new Input(s);
Output o = new Output(s);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
}
}
等待唤醒机制
* 远离轮询:经典的序列问题:一个线程正在生产数据而另一个线程在使用、消费数据,在轮询系统中,消费者在等待生产者生产数据时浪费CPU周期,而生产者也必须等待消费者工作结束才能继续启动,浪费更多CPU时间;
* wait(),notify(),notifyAll()方法实现线程间通信
- 仅在synchronized方法中才能被调用,因为要对持有监视器(锁)的线程操作
- wait():被调用的线程放弃管程,进入睡眠,放弃执行权,直到其他线程进入相同管程并调用notify(),这些wait()状态的锁按照先后顺序存放在线程池中;
- notify():恢复相同对象中(也就是使用同一个锁)第一个调用wait()的线程;
- notifyAll():恢复相同对象中所有调用wait()的线程;根据优先级判定运行顺序;
- 以上三个方法都定义在Object类中,因为这些方法在操纵同步中的线程时,都必须要标识该线程的锁,只有同一个锁上的睡眠,可以被同一个锁上的notify唤醒;而锁可以是任意对象,因此这些方法定义在Object类中;
synchronized{
while(true){
wait();
}
...
false;
notifyAll();
}
final boolean isAlive()
final void join() throws InterruptedException
final void setPriority(int level)
final int getPriority()
//Copyright @BJTShang
class clicker implements Runnable{
int click = 0;
Thread t;
private volatile boolean running = true;
public clicker(int p){
t = new Thread(this);
t.setPriority(p);
}
public void run(){
while(running){
click++;
}
}
public void start(){
t.start;
}
public void stop(){
running = false;
}
}
class HiLoPri{
public static void main(String[] args){
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
clicker hi = new clicker(Thread.NORM_PRIORITY + 2);
clicker lo = new clicker(Thread.NORM_PRIORITY - 2);
lo.start();
hi.start();
try{
Thread.sleep(10000);
}catch (InterruptedException e){
System.out.println("Main thread interrunpted.");
}
lo.stop();
hi.stop;
try{
hi.t.join();
lo.t.join();
}catch(InterruptedException e){
System.out.println("InterruptedException caught");
}
System.out.println("Low-priority thread: " + lo.click);
System.out.println("High-priority thread: " + hi.click);
}
}
running前的关键字volatile用来确保running的值在下面的循环中每次都得到验证;