一.多线程实现方式——继承Thread类
线程主体
Thread类是定义在java.lang包中,一个类只要继承Thread类,此类就称为多线程操作类,在Thread子类中,必须明确重写run()方法,此方法称为线程主体;Thread类定义
public class Thread extends Object implements Runnable;
从定义看,Thread类也是Runnable接口的子类。Thread总结
启动一个线程,必须使用Thread类中定义的start()方法,一旦调用start()方法,实际上最终调用的就是run()方法。通过run()方法启动线程其实就是调用一个类中的方法,当作普通方法的方式调用,并没有创建一个新线程,程序中依旧只有一个主线程,必须等到run()方法里面的代码块执行完毕,才会继续执行下面的代码,这样就没有达到多线程的目的。代码示例
class Thread01 extends Thread{
private String name;
public Thread01(String name){
this.name = name;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println(name + "运行,i=" + i);
}
}
}
public class Thread01_Thread{
public static void main(String[] args) {
Thread01 t1 = new Thread01("线程A");
Thread01 t2 = new Thread01("线程B");
t1.start();
t2.start();
}
}
二.多线程实现方式——实现Runnable接口
Runnable接口的实现
在java中也可以通过此方式实现多线程,Runnable接口中只定义了一个抽象方法run()。因此,通过Runnable接口实现多线程,只要实例化Runnable子类对象,实例化Thread对象,调用start()方法即可。Runnable接口总结
Thread类在操作多线程的时候,无法达到资源共享的目的,因为每个对象都包含各自的属性;而使用Runnable接口可以实现资源共享。代码示例
class Thread02 implements Runnable{
private String name;
public Thread02(String name){
this.name = name;
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(name + "运行,i=" + i);
}
}
}
public class Thread02_Runnable {
public static void main(String[] args) {
Thread02 t1 = new Thread02("线程A");
Thread02 t2 = new Thread02("线程B");
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
}
三.java中启动线程start()和run()方法的区别
线程的启动
java中启动线程有两种方法,继承Thread类和实现Runnable接口,由于java无法实现多继承,所以一般通过实现Runnable接口来创建线程。但是,不管使用哪种方法,都可以通过start()方法和run()方法来启动线程。start()方法
通过该方法启动线程的同时也创建了一个线程,真正实现了多线程,无需等待run()方法中的代码执行完毕,就可以接着执行下面的代码。此时,start()这个线程处于就绪状态,当得到CPU的时间片后就会执行其中的run()方法。这个run()方法包含了要执行的这个线程的内容,run()方法执行完毕,此线程也就终止了。run()方法
通过run()方法启动线程其实就是调用一个类中的方法,当作普通方法的方式调用,并没有创建一个新线程,程序中依旧只有一个主线程,必须等到run()方法里面的代码块执行完毕,才会继续执行下面的代码,这样就没有达到多线程的目的。
四.线程的强制运行
join()方法
在java多线程实现过程中,使用join()方法可以让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等此线程完成之后才可以运行。代码示例
class Thread04 implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName());
for(int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName() + "运行,i=" + i);
}
}
}
public class Thread04_join {
public static void main(String[] args) {
Thread04 td = new Thread04();
Thread t = new Thread(td, "nice");
t.start();
for(int i=0;i<50;i++){
if(i>10){
try {
t.join();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("main线程运行:" + i);
}
}
}
五.线程的休眠
sleep()方法
在实现多线程时,在程序中允许一个线程进行短暂的休眠,直接使用Thread.sleep()方法即可。代码示例
class Thread05 implements Runnable{
public void run() {
for(int i=0;i<10;i++){
try {
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行,i=" + i);
}
}
}
public class Thread05_sleep {
public static void main(String[] args) {
Thread05 td = new Thread05();
Thread t = new Thread(td, "nice");
t.start();
}
}
六.后台线程
后台线程
在java程序中,只要前台有一个程序在运行,则整个java进程就不会消失。因此,此时可以设置一个后台线程,这样即使java进程结束了,此后台线程依然可以继续执行。代码示例
class Thread06 implements Runnable{
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程运行...");
}
}
}
public class Thread06_setDeamon {
public static void main(String[] args) {
Thread06 td = new Thread06();
Thread t = new Thread(td, "nice");
t.start();
}
}
七.线程的优先级
setPriority()方法
在java的线程操作中,所有的线程在运行前都会保持就绪状态,那么此时,哪个线程的优先级高,哪个线程就有可能被优先执行。代码示例
class Thread07 implements Runnable{
public void run() {
for(int i=0;i<5;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行,i=" +i);
}
}
}
public class Thread07_priority {
public static void main(String[] args) {
Thread07 td = new Thread07();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
Thread t3 = new Thread(td, "线程C");
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t3.setPriority(Thread.NORM_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
八.线程的同步
1)经典问题分析
经典案例:售票
分析:票数出现负数的情况
因为在run()方法里面加入了延迟操作,那么一个线程就有可能还没对票数进行减操作之前,其他线程就已经将票数减少了,这样一来就会出现负数情况。死锁问题解决
想要解决资源共享的同步操作问题,可以使用同步代码块和同步方法两种方式完成;代码示例
class Thread08 implements Runnable{
private int ticket = 5;
public void run() {
for(int i=0;i<100;i++){
if(ticket>0){ // 还有票
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票:ticket = " + ticket--);
}
}
}
}
public class Thread08_Ticket {
public static void main(String[] args) {
Thread08 td = new Thread08();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
Thread t3 = new Thread(td, "线程C");
t1.start();
t2.start();
t3.start();
}
}
2)死锁问题解决——同步块
线程的同步:Synchronized
所谓的同步,就是指多个操作在同一时间段内只能有一个线程执行,其他线程要等待此线程完成后才可以继续执行。代码示例一同步块
class Thread04 implements Runnable{
private int ticket = 5;
public void run() {
for(int i=0;i<100;i++){
synchronized (this) { // 同步代码块:对当前对象进行同步
if(ticket>0){ // 还有票
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票:ticket = " + ticket--);
}
}
}
}
}
public class Thread04_Synchronized1 {
public static void main(String[] args) {
Thread04 td = new Thread04();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
Thread t3 = new Thread(td, "线程C");
t1.start();
t2.start();
t3.start();
}
}
3)死锁问题解决——同步方法
同步方法
除了可以将需要的代码设置为同步块之外,也可以使用Synchronized关键字将一个方法设置成同步方法。
格式:synchronized 方法返回值 方法名称(参数列表){}代码示例
class Thread05 implements Runnable{
private int ticket = 5;
public void run() {
for(int i=0;i<100;i++){
this.sale(); // 调用同步方法
}
}
public synchronized void sale(){
if(ticket>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + "卖票:ticket = " + ticket--);
}
}
}
public class Thread05_Synchronized2 {
public static void main(String[] args) {
Thread05 td = new Thread05();
Thread t1 = new Thread(td, "线程A");
Thread t2 = new Thread(td, "线程B");
Thread t3 = new Thread(td, "线程C");
t1.start();
t2.start();
t3.start();
}
}
九.线程的死锁
线程死锁
当两个线程相互调用Thread.join();
当两个线程使用嵌套的同步块时,一个线程占用了另一个线程的必需的锁,互相等待时被阻塞,就有可能出现死锁;代码示例
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("李四得到书了。") ;
}
};
public class Thread06_Deadlock implements Runnable{
private static Zhangsan zs = new Zhangsan() ;
private static Lisi ls = new Lisi() ;
private boolean flag = false ; // 标志位,判断那个先说话
public void run(){ // 覆写run()方法
if(flag){
synchronized(zs){ // 同步张三
zs.say() ;
try{
Thread.sleep(500) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
// 相互等待彼此对象同步锁执行结束,相互等待引起的死锁;
synchronized(ls){
zs.get() ;
}
}
}else{
synchronized(ls){
ls.say() ;
try{
Thread.sleep(500) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
synchronized(zs){
ls.get() ;
}
}
}
}
public static void main(String args[]){
Thread06_Deadlock t1 = new Thread06_Deadlock() ;
Thread06_Deadlock t2 = new Thread06_Deadlock() ;
t1.flag = true ;
t2.flag = false ;
Thread thA = new Thread(t1) ;
Thread thB = new Thread(t2) ;
thA.start() ;
thB.start() ;
}
};
十.线程的生命周期
生命周期
一个线程创建之后通过start()方法进入到运行状态,在运行状态中可以使用yield()进行礼让。但是仍然可以进行,如果现在一个线程需要暂停的话,可以使用suspend()、sleep()、wait(),如果现在线程不需要再执行,则可以通过stop()结束(如果run()方法执行完毕也表示结束),或一个新线程直接调用stop()方法也可以进行结束。以上有如下几个方法:
1)suspend():暂时挂机线程;
2)resume():恢复挂起的线程;
3)stop():停止线程;
因为以上三个方法都会产生死锁问题,所以现在已经不建议使用了。
现在停止线程的做法:通过设置标志位,让线程停止运行。