在说多线程之前,先说说什么是单线程吧。单线程就是程序按照代码书写顺序,函数调用顺序,一步一步而往下执行,直到程序结束,例如下面这样
package multi_thread;
public class SingleThreaded {
public void method1(String string) {
System.out.println("metho1开始");
System.out.println(string);
System.out.println("method2结束");
}
public void method2 (String string) {
System.out.println("method2开始");
method1(string);
System.out.println("method2结束");
}
public static void main(String[] args) {
SingleThreaded singleThreaded=new SingleThreaded();
singleThreaded.method2("hello");
}
}
相应的,多线程就是程序可能不按照程序书写顺序或者调用顺写来执行,它可能同时执行几个线程,或者a线程还未执行完就接着执行b线程了,两个交叉执行下去。很明显,控制两个线程交叉执行要比一个一个执行花费的时间可和资源要更多,那为什么还要进行多线程操作呢?因为当外设或调用的资源远更不上cpu运算时间时,做一个操作就会出现大量空闲时间,而在这些空闲时间处理别的线程就会很划算。
创建多线程的方式有两种,一种通过实现Runnable接口,一种通过继承Thread类。两者都需要实现一个run()方法,而在建立线程的时候需要创建Thread类的对象,例如: Thread myThread=new Thread(new MyThread());而在线程启动时不需要像调用普通方法那样调用run方法了,而是通过线程对象调用start方法来实现。
package multi_thread;
//线程的一种实现方式,通过实现Runnable接口,并重写run方法
public class MyRunner implements Runnable{
public void run() {
for (int i = 0; i <100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
package multi_thread;
//线程的一种实现方式,通过继承Thread类,并重写run方法
public class MyThread extends Thread {
Thread thread;
public MyThread () {
this(new Thread());
}
public MyThread (Thread thread) {
this.thread=thread;
}
public void run() {
//Thread.currentThread().setName("线程二");
for (int i = 0; i < 200; i++) {
System.out.println(this.getName()+":"+i+
thread.getName()+":"+thread.isAlive());
//获取当前线程名字,判断thread进程是否还活着(可运行、阻塞为true,新建、终止为false)
}
}
}
package multi_thread;
import java.nio.channels.NonWritableChannelException;
public class MultiThread {
public static void main(String[] args) {
System.out.println("主线程开始");
Thread thread1=new Thread(new MyRunner());//创建一个新线程
thread1.setDaemon(true);//守护线程会先于用户线程结束
thread1.setName("线程1");//设置线程名称
//Thread thread2=new Thread(new MyThread());
Thread thread2=new MyThread(thread1);//继承类的方式可以有两种创建方式
//thread2.setDaemon(true);
thread2.setName("线程2");
thread2.start();
thread1.start();//线程开始
System.out.println(thread1.getName()+"开始执行");
System.out.println(thread2.getName()+"开始执行");
System.out.println(Thread.currentThread().getName()+"结束");//主线程
}
}
线程可以分为守护线程和用户线程,可以通过setDaemon(boolean on)方法来实现,(true为守护线程),守护线程和用户线程的区别是:用户线程不能提前结束,而守护线程可以提前结束,前提是java虚拟机中没有用户线程在运行了。
package multi_thread;
public class ActRunner extends Thread{
private int n;
public ActRunner (int n) {
this.n=n;
}
public void run() {
for (int i = 0; i <n; i++) {
System.out.println(this.getName()+":"+i);
}
System.out.println(this.getName()+"结束");
}
}
package multi_thread;
public class TestSetDemo {
public static void main(String[] args) {
Thread thread1=new ActRunner(100);
Thread thread2=new ActRunner(400);
thread1.setName("用户线程");
thread2.setName("守护线程");
thread2.setDaemon(true);
//设置线程为守护线程要在线程开始之前设置,守护线程会先于用户线程结束之前结束
//thread1.setDaemon(true);
thread1.start();
thread2.start();
}
}
线程有他的生命周期,线程的生命周期一般可以分为六个阶段:新建(也就是被new的时候),可运行(正在java虚拟机执行的进程),阻塞(受阻塞,并等待某监视锁的进程),等待(等待另一线程执行某一特定操作的线程),超时等待(等待另一线程执行某一动作取决于等待时间的线程),终止(已经退出的线程)。isAlive方法时判断线程是否存活的方法,当线程运行或阻塞的时候为true,终止,新建为false。sleep用来使线程睡眠。
package multi_thread;
public class SleepThread extends Thread {
public void run() {
try {
Thread.sleep(100);//休眠100毫秒
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
/*try {
Thread.sleep(100);//休眠100毫秒
} catch (Exception e) {
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
package multi_thread;
public class SleepThreadTest {
public static void main(String[] args) {
System.out.println("主线程开始");
Thread thread1=new SleepThread();
Thread thread2=new Thread(new MyRunner());
thread1.setName("SleepThread");
thread2.setName("NormalThread");
thread1.start();
System.out.println("启动"+thread1.getName());
thread2.start();
System.out.println("启动"+thread2.getName());
System.out.println("主线程结束");
}
}
线程让步:Thread.yield()方法会暂停当前正在执行的线程对象,把执行机会让给相同或更高优先级的线程。java中线程优先级分为十级,数字越大代表优先级越高,默认为五级。可以通过setPriority()方法设置优先级,通过getPriorrity()得到优先级。
package multi_thread;
public class YieldThread extends Thread {
public void run() {
for (int i = 0; i <100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
if (i%5==0) {
Thread.yield();//线程让步
}
}
}
}
package multi_thread;
public class YieldThreadTest {
public static void main(String[] args) {
System.out.println("主线程开始");
Thread thread1=new Thread(new YieldThread());
Thread thread2=new Thread(new MyRunner());
thread1.setName("YieldThread");
thread2.setName("NormalRunner");
thread1.setPriority(Thread.MAX_PRIORITY);//将thread1的优先级设为最高
thread1.start();
System.out.println(thread1.getName()+"开始");
thread2.start();
System.out.println(thread2.getName()+"开始,优先级为"+thread2.getPriority());
System.out.println(Thread.currentThread().getName()+"结束");
}
}
线程的加入:Thread.join()方法可以使两个交叉执行的线程变为顺序执行,加入的子进程运行完成后,其他进程才能继续执行。
线程同步:如果多个线程都会调用修改同一数据的方法,那么这几个线程将会相互影响对方的行为,使结果不准确。其原因是,当一个线程执行到修改该数据的代码行时,不是一下就能修改好的,需要有步骤的:现将要修改的数据装入当前线程的寄存器中,再使当前线程寄存器上的数据变化,最后再将该数据重新赋值给原数据。在执行这几个步骤的过程中就可能出现线程还未执行完这几个步骤就开始了新的线程使得数据的修改造成紊乱。这样就需要线程同步,同步的思想就是把这几个步骤看做一个整体,只能在完成步骤或在步骤未开始之前执行别的线程,不能在步骤进行中执行别的线程。
线程同步的方法有两个,一是通过关键字synchronized,一是通过对象锁来进行线程同步。synchronized可以将一个方法设置为同步方法,只要在方法返回值前面加上synchronized即可。synchronized还可以使代码块为同步代码块,synchronized(对象){}。对象锁是将锁住的地方进行声明,任何时候锁住的地方最多只能有一个线程拥有。不过使用锁的时候要避免出现死锁现象,要加大锁的力度,不要分别同步各个资源的操作代码块,而是统一放在一个同步块中。
package multi_thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketOffice extends Thread {
private int tickets=0;
private Lock lock=new ReentrantLock();//通过对象锁来确保线程同步
public void run() {
boolean flag=true;
while (flag) {
flag=sell();
}
}
public boolean sell() {//使用关键字synchronized来确保线程同步
boolean flag=true;
//synchronized (this) {
lock.lock();
if(tickets<100){
tickets++;
try {
Thread.sleep(40);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"卖出了第"+tickets+"张票");
}else{
flag=true;
}
lock.unlock();
//}
return flag;
}
}
package multi_thread;
import java.awt.TrayIcon;
public class TicketTest {
public static void main(String[] args) {
TicketOffice ticketOffice=new TicketOffice();
Thread thread1=new Thread(ticketOffice);
Thread thread2=new Thread(ticketOffice);
Thread thread3=new Thread(ticketOffice);
Thread thread4=new Thread(ticketOffice);
thread1.setName("售票点1");
thread2.setName("售票点2");
thread3.setName("售票点3");
thread4.setName("售票点4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
package multi_thread;
import java.awt.image.renderable.RenderableImageOp;
import java.nio.channels.NetworkChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadLockTest implements Runnable {
public boolean flag=true;
private static Object res1=new Object();//资源1
private static Object res2=new Object();//资源2
public void run() {
if (flag) {
//锁定资源1
synchronized (res1) {
System.out.println("锁定资源1,等待资源2...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (res2) {
System.out.println("等到资源2");
}
//锁定资源2
}
}else {
//锁定资源2
synchronized (res2) {
System.out.println("锁定资源2,等待资源1.。。。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (res1) {
System.out.println("等到资源1");
}
}
//锁定资源1
}
}
public static void main(String[] args) {
DeadLockTest deadLockTest1=new DeadLockTest();
DeadLockTest deadLockTest2=new DeadLockTest();
deadLockTest2.flag=false;
Thread thread1=new Thread(deadLockTest1);
Thread thread2=new Thread(deadLockTest2);
thread1.start();
thread2.start();
}
}
wait和notify方法:wait和notify是java同步机制中重要的组成部分,与synchronized一起使用可以创造出许多优秀的同步机制。wait和notify方法只能在synchronized代码块中使用。
wait被调用时,当前进程将被中断,并且放弃该对象的锁。另外的线程执行了某个对象的notify()方法后,会唤醒在此对象等待池中的某个线程,使之成为可运行的线程。notifyAll()方法会唤醒所有等待这个对象的线程,使之成为可运行的线程。
package product;
public class Clerk {//店员
int index=0;
Products[]pro=new Products[10];
//生产者生产出来的产品交给店员
public synchronized void addProducts (Products pd) {
while (index==pro.length) {
try{
this.wait();//产品已满,停止生产
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.notify();//通知等候区的消费者取产品
pro[index]=pd;
index++;
}
//消费者从店员处取产品
public synchronized Products getProducts() {
if (index<=0) {
try {
//System.out.println(this.getClass());
this.wait();//缺货,等候取货
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getClass());
this.notify();//通知等待区的生产者开始生产产品
}
return pro[--index];
}
}
package product;
public class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk=clerk;
}
public void run() {
System.out.println("消费者开始取走产品");
for (int i = 0; i < 20; i++) {
Products pd=clerk.getProducts();
System.out.println("消费了:"+pd);
try {
Thread.sleep((int)(Math.random()*10)%100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package product;
public class Producter implements Runnable {
private Clerk clerk;
public Producter (Clerk clerk){
this.clerk=clerk;
}
public void run() {
System.out.println("生产者开始生产产品");
for (int i = 0; i <20; i++) {
Products pd=new Products(i);
clerk.addProducts(pd);
System.out.println("生产了:"+pd.toString());
try {
Thread.sleep((int)(Math.random())%100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package product;
public class Products {
int id;
public Products(int id){
this.id=id;
}
public String toString() {
return "Products:"+id;
}
}
package product;
public class ProductTest {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Thread producter=new Thread(new Producter(clerk));
Thread consumer=new Thread(new Consumer(clerk));
producter.start();
consumer.start();
}
}
用Timer类调度任务:Timer类代表一个计数器,与每一个Timer对象相对应的是单个后台线程,用于顺序的执行所有的计时器任务。Timer是个抽象类,要创建定时任务时,需要继承这个类,并实现run()方法,把要执行的任务放于run()方法体中或者使用匿名内部类进行计时器调度任务。常用方法为public void schedule(TimerTesk tesk,long delay,long period)重复的以固定的延迟时间执行以任务 public void scheduleAtFixedRate(TimerTesk tesk,long delay,long period)重复的以固定的频率执行以任务 public void cancel()终止此计数器,丢弃所有当前已安排任务。
package multi_thread;
import java.io.IOException;
import java.util.Timer;
import java.util.*;
public class TimeTest {
public static void main(String[] args) {
Timer timer=new Timer();
//MyTest task=new MyTest();
timer.scheduleAtFixedRate(new TimerTask() {//匿名内部类
@Override
public void run() {
System.out.println("起床了....");
}
}, 10, 1000);
while (true) {
try {
char ch=(char) System.in.read();
if (ch=='q'){
timer.cancel();
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}