线程的概念:线程是一个程序里面不同的执行路径,(一个进程里面有个主线程,叫main方法),在同一个时间点上,一个CPU只能支持一个线程在执行,java的线程是通过java.lang.thread来实现的
1:可以通过创建Thread的实例来创建新的线程
2:每个线程都是通过特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体,通过调用Thread类的start()方法来启动一个线程
进程的概念:进程是一个静态的概念
两种方式来创建新的线程
第一种:定义线程类实现Runnable接口
Thread myThread = new Thread(target);//target为Runnable接口类型
Runable中只有一个方法:public void run();用于定义线程运行体
使用Runnable接口可以为多个线程提供共享的数据
在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法 public static Thread currentThread() 获取当前线程的引用
第二种
可以定义一个Thread的子类并重写其run()方法,如
class MyThread extends Thread{
public void run(){}
}
然后生成该类的对象:MyThread myThread = new MyThread()
第一种创建线程的方式
//第一种创建线程的方式,实现Runnable接口,将实现了Runable接口的对象作为参数放到Thread线程中去,调用thread的start方法
public class TestThread1
{
public static void main(String[] args){
Runnable1 run1 = new Runnable1();
//run1.run();//若这样调用,相当于方法的调用,线程仍然只有一个main线程,先把Runnable1线程执行完之后然后再执行main方法中的线程
Thread thread1 = new Thread(run1);
thread1.start();//必须要调用thread中的start方法,相当于通知CPU。该线程已经准备就绪,当有时间时候请给与执行
for(int i = 0;i<100;i++){
System.out.println("main: "+i);
}
}
}
class Runnable1 implements Runnable
{
public void run(){
for(int i =0;i<100;i++){
System.out.println("Runnable1: "+i);
}
}
}
该程序中若是调用run1.run();程序的执行过程如下
若是调用Thread的start()方法,执行效果如下图
第二种创建线程的方式:
//第二种创建线程的方式,继承Thread类,直接调用该实现了Thread类的start()方法创建一个线程
public class TestThread2
{
public static void main(String[] args){
Runnable1 run1 = new Runnable1();
run1.start();
for(int i = 0;i<100;i++){
System.out.println("main: "+i);
}
}
}
class Runnable1 extends Thread
{
public void run(){
for(int i =0;i<100;i++){
System.out.println("Runnable1: "+i);
}
}
}
最好是采用第一种的实现接口的方式来创建线程,因为第一种方式比较灵活,可以继承某一个类,然后又可以实现其他的类,而第二种方式就只能是继承某一个类,一般都用第一种创建线程的方式
import java.util.*;
public class TestInterrupt
{
public static void main(String[] args){
MyThread thread = new MyThread();
thread.start();
try
{
Thread.sleep(10000);//这里的Thread是调用main主线程的Thread
}
catch (InterruptedException e)
{
}
thread.interrupt();//当过了10秒之后对打断该线程
}
}
class MyThread extends Thread //在这里只能用第二种创建线程的方式,因为第一种实现runnable接口的方式并没有sleep方法,只有Thread类才有sleep方法
{
public void run(){
while (true)
{
System.out.println("---"+new Date()+"---");
try
{
sleep(1000);
}
catch (InterruptedException e) //该异常必须捕获,不能从run方法中抛出,因为run方法是继承父类的方法,子类不能抛出父类为抛出的方法
{
return;
}
}
}
}
测试join()方法,join方法也是会抛出异常的,所以将异常进行捕获
//测试线程的join方法
public class TestJoin
{
public static void main(String[] args){
Thread2 myThread = new Thread2("abl");
myThread.start();
try
{
myThread.join(); //join()方法是相当于调用run方法,将方法合并到main线程中来,不是两个线程在跑
}
catch (InterruptedException e)
{
return ;
}
for(int i = 0;i<10;i++){
System.out.println("i am Thread");
}
}
}
class Thread2 extends Thread
{
Thread2(String s){
super(s);
}
public void run(){
for(int i =0 ;i<10;i++){
System.out.println("i am "+getName());
try
{
sleep(1000);
}
catch (InterruptedException e)
{
return ;
}
}
}
}
其中join方法的执行路线图如下所示:
测试yeild方法,执行该方法会让其他的线程先执行
//测试yield()方法,该方法的作用是暂停当前正在执行的线程对象,并执行其他线程
public class TestYeild
{
public static void main(String[] args){
Thread3 t1 = new Thread3("t1");//同一个线程类是可以new多个线程对象的
Thread3 t2 = new Thread3("t2");
t1.start();
t2.start();
}
}
class Thread3 extends Thread
{
Thread3(String s){
super(s);
}
public void run(){
for(int i=0;i<100;i++){
System.out.println("i name"+ i+ getName());
if(i%10==0){
yield();
}
}
}
}
测试线程的优先级
线程优先级的代码
//测试线程的优先级
public class TestPriority
{
public static void main(String[] args){
Thread t = new Thread(new Thread1());
Thread t3= new Thread(new Thread2());
t.setPriority(Thread.NORM_PRIORITY+3);
t.start();
t3.start();
}
}
class Thread1 implements Runnable
{
public void run(){
for(int i = 0;i<100;i++){
System.out.println("thread1 : "+i);
}
}
}
class Thread2 implements Runnable
{
public void run(){
for(int i =0;i<100;i++){
System.out.println("thread2: "+i);
}
}
}
正确的停止线程的方式:
//正确的停止线程的方式,最好不用stop方法,太简单粗暴,线程来不及关闭一个窗口就直接stop
public class StopThread
{
public static void main(String[] args){
RunTest r = new RunTest();
Thread t = new Thread(r);
t.start();
for(int i =0;i<100;i++){
System.out.println("----"+i);
}
r.shutDown();
}
}
class RunTest implements Runnable
{
boolean flag = true;
public void run(){
while(flag){
for(int i =0;i<100;i++){
System.out.println(i);
}
}
}
public void shutDown(){
flag = false;
}
}
线程互斥同步方式:
若想避免多个线程同时访问一个资源,就使用synchronized关键字来对对象进行锁定,不让其他的线程访问,直到该线程访问结束//测试线程同步
public class TestSync implements Runnable
{
TestTimer timer = new TestTimer();
public static void main(String[] args){
TestSync t = new TestSync();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
}
class TestTimer
{
private static int num = 0;
public /*synchronized*/ void add(String name){//也可以在方法前加上关键字synchronized来对该对象进行锁定
synchronized(this){ //通过synchronized关键字锁定当前对象,这样该代码资源会被锁定,在执行的过程中不被其他的线程所干扰
num++;
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
}
System.out.println(name+"是第"+num+"个人访问的");
}
}
}
线程死锁的代码:(有问题)
//测试线程死锁的例子
public class TestDeadLine implements Runnable
{
int flag =1;
Object o1 = new Object();
Object o2 = new Object();
public void run(){
System.out.println("flag:"+flag);
if(flag == 1){
synchronized(o1){
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
synchronized(o2){
System.out.println(0);
}
}
}
if(flag == 0){
synchronized(o2){
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
synchronized(o1){
System.out.println(1);
}
}
}
}
public static void main(String[] args){
TestDeadLine tdl1= new TestDeadLine();
TestDeadLine tdl2 = new TestDeadLine();
tdl1.flag=1;
tdl2.flag=0;
Thread t = new Thread(tdl1);
Thread t2 = new Thread(tdl2);
t.start();
t2.start();
}
}
加锁例子:
//面试题:当m1在执行的时候,m2能够被执行吗 :结果是能够执行m2方法,,原因是因为m1方法使用了加锁,当前的对象不能访问m1方法,但是可以允许其他的对象访问没有加锁的代码段
public class TT implements Runnable
{
int b =100;
public synchronized void m1()throws Exception{
b=1000;
Thread.sleep(500);
System.out.println("b="+b);
}
public void m2(){
System.out.println(b);
}
public void run(){
try
{
m1();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args){
TT tt= new TT();
Thread t = new Thread(tt);
t.start();
Thread.sleep(1000);
tt.m2();//若输出结果b是100的话则在m1()执行的过程中是不能执行m2方法的,
//因为m1方法中将b修改为1000,但是m1还没有解锁,所以m2只能看到100;
}
}
生产者和消费者经典问题的代码
public class ProduceConsumer
{
public static void main(String[] args){
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(c).start();
}
}
class WoTou //定义馒头类
{
int id;
WoTou(int id){
this.id = id;
}
public String toString(){
return "WoTou:"+id;
}
}
class SyncStack //定义装馒头的筐子
{
int index = 0;
WoTou[] arrWT = new WoTou[6];
public synchronized void push(WoTou wt){
while(index == arrWT.length){
try
{
this.wait();//让当前正在访问该对象的线程等待(该方法必须是synchronized方法,才能使用wait方法)
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
this.notify();//叫醒一个当前对象中等待的线程的方法,一般是和wait配套使用(不是叫醒这个push方法,而是叫醒pop方法)
arrWT[index] = wt;
index++;
}
public synchronized WoTou pop(){
while(index ==0){
try
{
this.wait();//让当前正在访问该对象的线程等待(该方法必须是synchronized方法,才能使用wait方法)
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
this.notify();//若有多个生产者在生产者,这里就用notifyAll();
index--;
return arrWT[index];
}
}
class Producer implements Runnable
{
SyncStack ss = null;
Producer(SyncStack ss){
this.ss = ss;
}
public void run(){
for (int i =0;i<20 ;i++ )
{
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生产了:"+wt);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e )
{
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable
{
SyncStack ss = null;
public Consumer(SyncStack ss){
this.ss = ss;
}
public void run(){
for (int i=0;i<20 ;i++ )
{
WoTou wt = ss.pop();
System.out.println("消费了:"+wt);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e )
{
e.printStackTrace();
}
System.out.println(wt);
}
}
}
sleep()方法和wait()方法的区别。
第一:sleep()方法是Thread类中的方法,wait()方法是object中的方法
第二:sleep()方法时别的线程不可以访问锁定的对象,wait()方法的时候别的线程可以访问锁定对象,调用wait方法的时候必须要锁定该对象