这一篇是线程的中级篇吧,还没有涉及到高级篇,高级篇,涉及到的原理比较多,中级篇,还是仅仅停留在多线程的使用上,只不过,使用的层次提高了很多。
中级使用1.(将两种线程创建的方式合二为一)
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("runable.run");
}
}){
public void run() {
System.out.println("Thread.run");
};
}.start();
上面代码的输出的是结果是什么?输出的是Thread.run
因为,new Thread(Runable.run){run()}.start();,new Threa(){run()}是一个Thread的子类对象,start(),jvm调用的是子类对象的run()方法,如果子类没有run()方法,则会调用父类的run()方法,父类的run()方法会调用Runable.run()方法。
中级使用2.定时器
//1.简单使用
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("haha");
}
}, 1000);//一个定时器,调度(一个任务,设置时间)
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("haha");
}
}, 1000,2000);//一个定时器,调度(一个任务,设置时间,间隔时间持续执行)
//2.深入使用
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("haha");
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("xixi");
}
}, 1000,2000);
}
}, 1000);//一个定时器执行一个任务,这个任务内部调用另一个定时器
中级使用3.互斥(synchronized)
我们都知道synchronized是互斥,可以用于代码块和方法上,使其安全,就是说,一个线程在使用使,其他线程无法进行。没有什么,最重要的是使用相同的对象,才可以使之互斥。
synchronized是一种互斥锁,一次仅允许一个线程进入被锁住的代码
synchronized是一种内置锁/监视器锁,java中每个对象都有一个内置锁,通过使用内置锁将将代码锁住。
synchronized保证了原子性和可见性。
例子:当一个synchronized代码块,和一个静态的synchronized方法,如何保证两个互斥?
要互斥,则必须使用相同的对象,如何保证对象相同呢?静态方法可以根据类名.方法名进行调用呢?所以,可以使用类.class,synchronized(类.class),类.class在内存中只有一份,所以可以使之互斥。
这是我在网上找的线程视频,是张孝祥老师讲的视频,里面有个题,子线程循环10次,主线程100次,子线程在循环10次,主线程再循环100次,就这样总共循环50次。这个题本来也是没有什么思路,然后就看视频,把思路学了一下。下面是这个题的代码。
写完这个代码,受教了。感觉对线程,有了更深的认识。对于线程互斥,组主要的还是那个锁对象,所以必须要找准那个对象。
public class ThreadDemo4 {
//子线程循环10次,主线程100次,子线程在循环10次,主线程再循环100次,就这样总共循环50次
public static void main(String[] args) {
final DemoT demo=new DemoT();
new Thread(new Runnable() {
public void run() {
for(int j=0;j<50;j++){//也就是说,如果demo被等待,线程的转态仅仅是运行变为阻塞,比如循环第20次等待,主线程
demo.sub(j);//获得执行权,之后,子线程被唤醒,还是第二十次循环开始的,这并没有改变
}
}
}).start();//子线程
for(int i=0;i<50;i++){
demo.mai(i);//主线程,可以看mai(),并没有创建一个线程,确确实实是main的主线程,因为mai()方法中有一个方法,
//this.wait(),相当于在main中调用了该方法,此时main主线程被等待,
}
}
}
class DemoT{
private boolean a=true;
public synchronized void sub(int j){
//为ture时,循环,否则,等待
while(!a){/这里while比if效果好,等待后,while会再次判断
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i=0;i<10;i++){
System.out.println("子线程第"+i+"次"+"-------"+j);
}
a=false;
this.notify();
}
public synchronized void mai(int j){
//为false时,循环,否则,等待
while(!a){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i=0;i<100;i++){
System.out.println("主线程第"+i+"次"+"-------"+j);
}
a=true;
this.notify();
}
}
写这个代码的时候,我在想,为什么不把循环50次的代码,也写到synchronized的方法体内?
如果写进入synchronized方法体内,一个线程获得锁后,其他线程无法进入,将这样,外部循环一次,内部循环10次,直至循环50次。与我们的题目不符合。
对象锁:要用到共同数据的若干个方法,应该归在同一个类中。体现了健壮性和高类聚。
中级使用4.线程内的共享变量(就是一个线程绑定一个数据源,这个线程内的对象,都可以调用这个数据源。
class DemoThread{
private static int data=0;
private static Map map=new HashMap<>();
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int data=new Random().nextInt();
System.out.println(Thread.currentThread().getName()+"+++++"+data);
map.put(Thread.currentThread().getName(), data);
new A().get();
new B().get();
}
}).start();
}
}
static class A{
public void get(){
int data=(int)map.get(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName()+"---"+data);
}
}
static class B{
public void get(){
int data=(int)map.get(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName()+"---"+data);
}
}
}
不同的线程具有不同的数据,就是一个Map,key值为线程名称。就是这种思想。上面只是一种模拟,当然,已经为我们提供了这样一个类,Threadlocal类,内部是一个Map集合,只需要设置值即可。当然,一个线程只能在Threadlocal绑定一个数据,但是我们可以将多个属性定义在一个类中,将这个类的对象设置值,放置在Threadlocal中就可以了,使用更简单。
//单例模式
class MyThreadScopeData{
private MyThreadScopeData(){}
public static /*synchronized*/ MyThreadScopeData getThreadInstance(){
MyThreadScopeData instance = map.get();
if(instance == null){
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
}
//private static MyThreadScopeData instance = null;//new MyThreadScopeData();//饱汉模式和饿汉模式
private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
/*
* 饱汉模式,对象还没有加载,就有了对象 static new
* 饿汉模式,只有要使用时,才创建对象
*/
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
中级使用5.多线程之间的共享变量
1.多个线程使用同一个实现了Runable的对象,前面买票案例就是这种方法。
2.static修饰的变量,多个线程均可操作
3.每个线程要操作的代码不同,需要将这几个方法封装在同一个实现了Runable类中,创建这个类的final的对象,创建线程时,传入不同的runable对象,在run()方法内,调用不同的方法。不同的是,run()方法内调用的方法不同,就这点区别。
4.一个runable类A封装不同的方法和数据,另一个runable类B的run()调用new A中的一个方法,另一个runable类C的run()调用new A中的一个方法,然后就是Thread创建的时候使用B或C对象
中级使用6.线程池Executors
java5提供类线程池,线程池分为三种:固定线程池(线程数固定),缓存线程池(动态的,根据任务创建线程),单一线程池(只有一个线程)-----单一线程池,运行完后,线程死亡,再有任务,还会在创建一个线程。
线程池的关闭:shotdown,所有任务完成后,关闭
shotdownNow,立即关闭,即使任务没有完成
中级使用7.线程锁Lock,比synchronized更加面向对象。
线程锁还有读写锁,分为读锁,写锁,读写锁三种。
和Lock的用法差不读,读锁,读数据时,上读锁,写数据时,上写锁。
中级使用8.condition
在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。
condition是基于Lock的,不能和synchronized联用的,感觉和wait()方法和notify()差不多,他的两个方法是await()和signal()方法,await()就是等待,signal()就是发送一个信号,唤醒一个线程。
传统的唤醒和等待,可以使用两个线程间的通信,但是三个线程之间进行通信如何呢?如果还是使用传统的wait()和notify()方法已经不能满足了,所以可以使用condition,功能更加强大。
三个线程之间的通信,可以创建三个condition对象,每个线程可以使用一个condition来控制线程的等待和唤醒。
中级使用9.Semaphore的使用
Semaphore信号灯,类似于,多人去银行取款(多个线程),2个柜台(2个信号灯),不管多少个人,取款的人数始终都是两(线程运行数始终都是2),一个人取款后(一个线程运行完后),再来一个人(线程)进行取款操作。常和线程池连用。
acquire(),从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
release(),释放一个许可,将其返回给信号量。
这一篇是线程使用的中级篇,其实就是在使用方法上介绍了一下,并没有举例子。可以看一下jdk api,tomcat,缓存的底层都有线程的身影,线程可谓是基本。关于线程,我还想写一篇线程高级篇,就不在仅仅是线程的使用了,会更深入一点。这个初级,中级,高级,仅仅是根据自己的水平定义的,自己所涉及的线程使用还是很少的,嗯,就这样了。