线程
注意:
- run()方法和start()方法的区别
run方法相当于普通方法调用,还是单线程,start方法才是多线程,启动线程由jvm运行run方法 - java的线程调度模型,使用的是优先级抢占式调度
- 接口实现会导致无法使用Thread类中一些方法,可使用Thread.currentThread.方法名来继续使用
多线程有两种实现方式
方式1:继承Thread类:class test2 extends Thread
方式2:实现runable接口:class test2 implements Runnable
runnable接口实现多线程有点特殊
// Runnable接口实现线程方式有点不一样
test2 t2= new test2();
Thread t3=new Thread(t2);
Thread t4=new Thread(t2);
t3.start();
t4.start();
继承Thread类实现多线程
public class test1 {
public static void main(String[] args){
test2 t2=new test2();
t2.start();
while (true)
{
System.out.println("this is main");
}
}
}
class test2 extends Thread
{
@Override
public void run() {
while (true)
{
System.out.println("我是线程");
}
}
}
Thread方法
getName()
setName()
可以看到,可以设置线程同名
public class test1 {
public static void main(String[] args){
test2 t2=new test2();
System.out.println(t2.getName());
// 给t2线程设置名称
t2.setName(new String("张三"));
System.out.println(t2.getName());
// 给俩线程设置同名会如何?
test2 t3=new test2();
t3.setName(new String("张三"));
System.out.println(t3.getName());
System.out.println(t2.getName());
}
}
带参构造方法
Thread类中有带参构造可以直接指定线程名,但是我们Thread子类test2没有带参构造
所以需要写出带参构造,直接传给super就行
public class test1 {
public static void main(String[] args){
test2 t2=new test2("线程1");
System.out.println(t2.getName());
}
}
class test2 extends Thread
{
test2(String name)
{
super(name);
}
//..............
currentThread()
Thread中的静态方法,用于返回当前正在执行的线程对象
public class test1 {
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());
}
}
线程优先级
我们知道java中的线程使用的是抢占式线程调度方式,所以需要根据优先级来判断线程的执行顺序
线程默认优先级:5
线程最低优先级:1
线程最高优先级:10
优先级,只能说明它获取cpu资源概率高,而并非一定,所以依然会出现,优先级低的先运行
getPriority()
setPriority(int)
使用该两方法设置线程的优先级
public class test1 {
public static void main(String[] args){
test2 t2=new test2("优先线程");
test2 t3=new test2("后置线程");
System.out.println(t2.getPriority()+" "+t3.getPriority());
t2.setPriority(10);
System.out.println(t2.getPriority()+" "+t3.getPriority());
// 初步运行,由于t2还没进入运行,所以先执行了t3,t2一进入,就抢占了t3的运行,直到t2运行结束
t3.start();
t2.start();
}
}
Sleep方法
需要抛出异常
Thread.Sleep(long time);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
join方法
只有join的线程结束后,其他线程才可以执行
返祖操作,返回单线程
public class test1 {
public static void main(String[] args) throws InterruptedException {
test2 t2=new test2("优先线程");
test2 t3=new test2("后置线程");
t2.start();
t2.join();// 设置只有t2运行完了,t3才可以运行
t3.start();
}
}
setDaemon,设置守护线程
主线程挂它也挂,当只剩下守护进程,程序结束
public class test1 {
public static void main(String[] args) throws InterruptedException {
test2 t2=new test2("优先线程");
test2 t3=new test2("后置线程");
// 设置为守护线程
t2.setDaemon(true);
t3.setDaemon(true);
t2.start();
t3.start();
}
}
同步代码块,同步锁
为了使多线程修改同一数据不会造成:读脏数据,重复修改,等错误
给修改数据的代码块加锁
obj是同一个,就代表同一把锁
synchronized (obj) {被枷锁代码}
public class test1 {
public static void main(String[] args) throws InterruptedException {
// Runnable接口实现线程方式有点不一样
test2 t2= new test2();
Thread t3=new Thread(t2);
Thread t4=new Thread(t2);
t3.start();
t4.start();
}
}
class test2 implements Runnable
{
public static int num=100;
// 这个obj对象随便new的
public Object obj=new Object();
test2()
{}
@Override
public void run() {
while (num>0)
{
// 被加锁的代码块,一次只能有一个线程在执行
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + " " + num);
num--;
}
}
}
}
自动同步的数据结构(线程安全),都是使用synchronized 实现同步
StringBuffer
vector
Hashtable
使用这几个结构,将会自动同步
Lock锁,Lock接口
更贴近自然语言的方式
Lock lock=new ReentrantLock();
lock.lock();
// 这部分的代码将被锁住
// 这部分代码一次只运行一个线程运行
lock.unlock();
如果在lock中间部分出现错误,unlock将无法执行到
所以,最好在外面套一层try catch
线程阻塞与执行
要注意,这些操作是对本线程内的,也就是说,如果wait了,就不执行下面的语句了
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
notifyAll();