线程安全和线程不安全
- 线程安全:在Java的线程中进行加锁操作,当一个线程访问该资源的时候只能等到加锁的线程使用完资源后别的线程才能使用该线程。不会出现数据不一致或者产生数据污染等问题。
- 线程不安全:对线程没有进行加锁操作有可能多个线程对数据操作,产生数据的污染等问题。
java中常见的线程安全和非安全的示例
- ArrayList是非线程安全的、Vector是线程安全的 。
- HashMap是非线程安全的、HashTable是线程安全的。
- StringBuilder是非线程安全的、StringBuffer是线程安全的。
- 线程不安全如下所示:代码运行结果如下
/**
* 线程不安全
* */
public class ThreadRunnable {
private static final Logger logger = LoggerFactory.getLogger(ThreadRunnable.class);
/**
* 相同代码公用一个资源
*
* @param args
*/
public static void main(String[] args) {
int num = 10;
ByTicketsRunnable byTicketsRunnable = new ByTicketsRunnable(num);
for (int i = 0; i < 11; i++) {
Thread thread = new Thread(byTicketsRunnable);
thread.start();
if (Thread.holdsLock(Thread.currentThread())) {
logger.info("当前线程持有对象监视器!");
}
}
}
}
class ByTicketsRunnable implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ByTicketsRunnable.class);
private int num;
public ByTicketsRunnable(int num) {
this.num = num;
}
@Override
public void run() {
synchronized (this) {
if (num > 0) {
num--;
logger.info("Thread {} 买到一张票 还剩:{} 张票", Thread.currentThread().getId(), num);
} else {
logger.info("Thread {} 没有抢到票 还剩:{} 张票", Thread.currentThread().getId(), num);
}
}
}
}
实现线程安全
- synchronized锁修饰后当方法抛出异常或者遇到return时候都会释放锁。
public class LockTest1 {
//单例对象
private static Test1 test = null;
//静态的方法
public static synchronized Test1 getTest1(){
if(test==null){
test=new Test1();
}
return test;
}
}
class Test1 {
}
- ReentrabtLock:支持可重入的锁
Java中如何保证线程的安全
通过合理的时间调度,避免共享资源的存取冲突。设计一个规则保证一个计算工作只交给一个线程去完成,而不是把一个计算工作交个多个线程去实现。
线程的基本状态以及状态之间的关系
- Running:运行状态
- Runnable:就绪状态(万事具备只歉Cpu)
- Blocked:表示阻塞状态(调用wait方法进入等到池子,调用sleep方法等待休眠或者其他进程结束,或是发生I/O中断)。
举例说明同步和异步
- 如果系统中存在临界资源、例如正在写的数据被另一个线程读到,那么这些数据就必须进行同步存取。
- 当应用程序在对象的调用上花了很长时间来执行,但是不希望让程序等待方法的返回。
实现线程安全
private int j = 0;
public static void main(String[] args) {
MultiThreadTest1 multiThreadTest1 = new MultiThreadTest1();
mt1 a = multiThreadTest1.new mt1();
mt2 b = multiThreadTest1.new mt2();
mt3 c = multiThreadTest1.new mt3();
mt4 d = multiThreadTest1.new mt4();
a.start();
b.start();
c.start();
d.start();
}
private synchronized void add() {
j++;
System.out.println("当前的线程为:" + Thread.currentThread().getName() + "=====" + "add:" + j);
}
private synchronized void del() {
j--;
System.out.println("当前的线程为:" + Thread.currentThread().getName() + "=====" + "del:" + j);
}
class mt1 extends Thread {
@Override
public void run() {
System.out.println("我是线程1");
for (int i = 0; i <= 100; i++) {
add();
}
}
}
class mt3 extends Thread {
@Override
public void run() {
System.out.println("我是线程3");
for (int i = 0; i <= 100; i++) {
del();
}
}
}
sleep()、wait()、notify()方法
- sleep():是Thread的方法。导致此线程暂停执行指定时间,把执行机会让给别的线程到时候自动恢复调用sleep()不会释放对象锁。
- wait():是Object的方法。次对象调用wait()方法会释放对象锁。
- notify():本线程才进入线程池准备获得对象锁进入运行状态。
Java中的线程池类型
- newFixedThreadPool(n):创建一个指定工作线程数量的线程池,如果线程数量达到初始化大小,则将提交的任务保存到池队列中。提高效率节省开销,不会释放空闲资源。
- newCachedThreadPool():缓存线程池,可以灵活收回空闲线程,若无可回收则创建新的。默认为1分钟。
- newSingThreadExcutor():只创建唯一的工作线程来执行任务,保证线程按照指定的书序执行,保证顺序的执行任务。
- newScheduledThreadPool(n):支持定时及周期性任务执行。
/**
* 线程池的种类
* */
@Test
public void test1(){
//缓冲线程池
ExecutorService executorService=Executors.newCachedThreadPool();
//指定工作量线程池子
ExecutorService executorService1=Executors.newFixedThreadPool(3);
//周期性任务线程池
ExecutorService executorService2=Executors.newScheduledThreadPool(3);
//单任务线程池子
ExecutorService executorService3=Executors.newSingleThreadExecutor();
}
java锁
- 乐观锁:读多写少。更新操作的时候上锁。
- 悲观锁:写多杜少。读写数据都会上锁。
- 自旋锁:如果持有锁的线程能在短时间内释放锁,那么等待的线程不需要阻塞挂起,他们只需要等一等,等持有锁的线程释放锁后立即获取锁。避免线程和内核的切换之间的消耗
- synchronized同步锁:是一种独占锁,同时属于可重入锁。
Synchronized和Lock
- Synchronized是java的关键字,当它修饰一个方法或者代码块时,能保证同一时刻只有一个线程运行改代码块。发生异常时候自动释放占有的锁,不会发生死锁现象
- Lock在发生异常时候如果没有通过unlock()去释放锁则很可能造成死锁现象。使用Lock需要在finally中释放锁。Lock可以让等待的线程响应中断。使用Synchronized只能是等待的线程一直等待下去。
集成Thread重写run()
public class TestExtendsThread {
static class Test1ExtendsThread extends Thread {
@Override
public void run() {
System.out.println("我是Test1ExtendsThread的线程!");
}
}
public static void main(String[] args) {
new Test1ExtendsThread().start();
}
}
实现Runnable接口
public class TestImplementsRunnable {
static class ThreadImplementsTunnable implements Runnable{
@Override
public void run() {
System.out.println("Hellow");
}
}
public static void main(String[] args) {
ThreadImplementsTunnable thread=new ThreadImplementsTunnable();
thread.run();
}
}
实现Callable接口
public class TestImplementsCallable {
static class T1 implements Callable{
@Override
public Object call() throws Exception {
System.out.println("Hellow");
return null;
}
}
public static void main(String[] args) {
T1 t1=new T1();
FutureTask futureTask=new FutureTask<>(t1);
futureTask.run();
}
}
- 上面的三种方式:频繁的创建、销毁对象消耗性能;导致占用过多的资源。
线程池的使用
- 线程池和数据库连接池有类似的功效,可以控制线程的数量。可以使线程的使用率提升。减少对象的创建和销毁,可以合理的使用线程资源,保证资源的使用效益最大。
线程池的四种类型以及使用方法
newFixedThreadPool
- 创建一个指定工作线程数量的线程池,如果线程数量达到初始化大小,则将提交的任务保存到池队列中。提高效率节省开销,不会释放空闲资源。
public class FixThreadPool {
public static void method_01() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
int index = i;
executor.execute(() -> {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + index);
});
}
executor.shutdown();
}
public static void main(String[] args) {
try {
method_01();
}catch (Exception e){
e.printStackTrace();
}
}
}
newCachedThreadPool
- 缓存线程池,可以灵活收回空闲线程,若无可回收则创建新的。默认为1分钟。
public class CachedThreadPoolTest {
public void method() {
try {
this.doOne(5);
this.doTwo(6);
}catch (Exception e){
e.printStackTrace();
}
}
private void doTwo(int x) throws Exception{
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < x; i++) {
final int index = i;
Thread.sleep(4000);
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " " + index);
}
});
}
}
private void doOne(int y) throws Exception {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < y; i++) {
final int index = i;
Thread.sleep(4000);
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " " + index);
}
});
}
}
public static void main(String[] args) {
CachedThreadPoolTest cachedThreadPoolTest=new CachedThreadPoolTest();
cachedThreadPoolTest.method();
}
}
newScheduledThreadPool
- 支持定时及周期性任务执行。
public class ScheduledThreadPoolTest {
public static void method_02() {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
long start = new Date().getTime();
System.out.println("scheduleAtFixedRate 开始执行时间:" + DateFormat.getTimeInstance().format(new Date()));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = new Date().getTime();
System.out.println("scheduleAtFixedRate 执行花费时间=" + (end - start) / 1000 + "m");
System.out.println("scheduleAtFixedRate 执行完成时间:" + DateFormat.getTimeInstance().format(new Date()));
System.out.println("======================================");
}
}, 1, 5, TimeUnit.SECONDS);
}
public static void main(String[] args) {
method_02();
}
}
newSingleThreadExecutor
- 只创建唯一的工作线程来执行任务,保证线程按照指定的书序执行,保证顺序的执行任务。
public class SingleThreadExecutorTest {
public static void method_04() {
try {
ExecutorService executor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int index = i;
executor.execute(() -> {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + index);
});
}
executor.shutdown();
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
method_04();
}
}