一、线程的理解
进程是程序的一种动态执行的实现,一个进程的实现需要多个线程的启动,线程与线程之间都是各自独立的。表面看是同时执行的,但其实是CPU会为它们分配时间片,并不是同时进行的。
二、创建线程的两种方式
(1)继承Thread
public class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
public void run(){
for(int i=1;i<=10;i++){
System.out.println(name+"运行"+i);
}
}
public static void main(String[] args) {
MyThread m1 = new MyThread("线程A");
MyThread m2 = new MyThread("线程B");
m1.start();
m2.start();
}
}
问题1:为什么不调用run()而调用start()呢?
答:因为通过Thread源码我们知道,实际调用的是start0(),它是native修饰的,它代表本机的操作系统函数,线程的启动需要底层操作系统的支持
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
问题2:同一线程可不可以多次调用start()呢?
答:不能,否则会报错 ---- IllegalThreadStateException
private volatile int threadStatus = 0;
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
问题3:必须要覆盖run()吗?
答:必须要覆盖,虽然Thread是实现Runnable接口的子类,但并没有完全实现run方法,此方法是Runnable的子类实现的,所以必须覆盖run方法
@Override
public void run() {
if (target != null) {
target.run();
}
}
(2)实现Runnable接口
public class MyThread2 implements Runnable{
private String name;
public MyThread2(String name){
this.name = name;
}
@Override
public void run() {
for(int i=1;i<=10;i++){
System.out.println(name+"运行"+i);
}
}
public static void main(String[] args) {
MyThread2 m1 = new MyThread2("a");
MyThread2 m2 = new MyThread2("b");
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m2);
t1.start();
t2.start();
}
}
注意:因为Runnable接口中只有抽象的run方法,所以启动还是需要Thread类,Thread类中有接收Runnable子类的构造器。
三、资源共享问题
单继承Thread资源不会共享,Runnable实现类会资源共享
四、线程的状态
创建、就绪、运行、阻塞、终止
1.创建:Thread t = new Thread();就是创建一个线程,但该线程只是有了内存空间并不具备运行能力
2.就绪:执行start()方法,具备了运行条件,在线程队列排队,等待CPU调度
3.运行:当前就绪的线程被调度并获得处理器资源,执行run()方法
4.阻塞:运行中的线程被人为的挂起,如sleep,wait等,进入阻塞状态,只有取消后才能进到就绪状态
5.终止:线程执行stop()或者run()执行完毕才会终止
问题:JAVA程序每次运行至少启动几个线程??
答:至少2个,一个是main线程,一个是垃圾收集线程。
具体说明:每当用java命令执行一个类时,都会启动一个JVM,每一个JVM实际上就是在操作系统上启动了一个进程,java本身具有垃圾收集的机制。
五、相关方法
(1)线程的强制运行 join()
可以让一个线程强制运行,其他线程必须等它执行完毕后才能执行
(2)线程的休眠sleep()
可以有延迟的效果
(3)中断线程 interrupt()
当一个线程执行时,另一个线程可以用该方法中断它
(4)后台线程
设置后台进程:setDaemon()
只要有一个前台线程没结束,JAVA进程就不会结束,后台进程是就算JAVA进程结束了,后台进程可以依然执行
(5)线程的优先级
最低: MIN 1 中等(默认,main线程): NORM 5 最高: MAX 10
优先级越高,被CPU优先调度的机会越大,就越先执行
(6)线程礼让 yield()
将一个线程的操作暂时让给其他线程执行
六、同步和死锁
(1)同步:如果是通过实现Runnable实现线程,操作类下的属性会资源共享,多个线程操作同一资源会出现资源同步问题。
解决办法:
同步代码块
synchronized(对象){}
同步方法
synchronized 返回值 方法名(){}
java定义方法完整格式: 访问权限 final static synchronized.....
public class 资源同步问题 {
public static void main(String[] args) {
MyThread21 t1 = new MyThread21();
new Thread(t1, "A").start();
new Thread(t1, "B").start();
new Thread(t1, "C").start();
}
}
class MyThread21 implements Runnable{
private int ticket = 5;
@Override
public void run() {
for(int i=0;i<100;i++){
this.sale();
}
}
public synchronized void sale(){
if(ticket>0){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行---卖票:"+ticket--);
}
}
}
加了同步锁后不会产生卖票为负数的情况了
(2)死锁
同步可以保证同一资源的正确性,但是过多的同步容易导致死锁
/**
* 张三:李四先把书给我,我再给他画
* 李四:张三先把画给我,我再给他书
* @author admin
*
*/
public class 死锁问题 {
public static void main(String[] args) {
DeadThread dt1 = new DeadThread();
DeadThread dt2 = new DeadThread();
dt1.flag = true;
dt2.flag = false;
new Thread(dt1,"线程A").start();
new Thread(dt2,"线程B").start();
}
}
class Zhangsan {
public void say(){
System.out.println("李四先把书给我,我再给他画");
}
public void get(){
System.out.println("李四把书还我了");
}
}
class Lisi {
public void say(){
System.out.println("张三先把画给我,我再给他书");
}
public void get(){
System.out.println("张三把画还我了");
}
}
class DeadThread implements Runnable{
private static Zhangsan zs = new Zhangsan();
private static Lisi ls = new Lisi();
boolean flag = false;
@Override
public void run() {
if(flag){
synchronized(zs){
zs.say();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(ls){
zs.get();
}
}
}else{
synchronized(ls){
ls.say();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(zs){
ls.get();
}
}
}
}
}
结果:两者都想让对方执行完,一直僵持不能往下进行。
七、消费者和生产者
synchronized wait notify 解决
Object类对线程的支持--》等待和唤醒,wait():进程等待
notify():唤醒一个 notifyAll():唤醒全部
注意:等待按顺序排列,但如果使用notifyAll就看谁的优先权高了
wait时,别人可以访问锁对象,sleep时,别人不可以访问锁对象!!!
public class Info {
String name = "TOM";
String content = "飞行员";
boolean flag = false;
public synchronized void set(String name,String content){
if(!flag){
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.content = content;
flag = false;
super.notify();
}
public synchronized void get(){
if(flag){
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(this.getName()+"-->"+this.getContent());
flag =true;
super.notify();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public class Consumer implements Runnable{
private Info info = null;
public Consumer(Info info){
this.info = info;
}
@Override
public void run() {
for(int i = 0;i<50;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.info.get();
}
}
}
public class Productor implements Runnable{
private Info info = null;
public Productor(Info info){
this.info = info;
}
@Override
public void run() {
boolean flag = false;
for(int i=0;i<50;i++){
if(flag){
this.info.set("Tom", "飞行员");
flag = false;
}else{
this.info.set("Sam", "宇航员");
flag = true;
}
}
}
}
public class Main {
public static void main(String[] args) {
Info info = new Info();
Productor pro = new Productor(info);
Consumer con = new Consumer(info);
new Thread(pro).start();
new Thread(con).start();
}
}
八、线程的生命周期
suspend():暂时挂起线程
resume():恢复挂起的线程
stop():停止线程
不推荐使用!!!若想停止线程,可以使用标志位